<template>
  <div>
    <infoText></infoText>
    <div id="stage"></div>
  </div>
</template>

<script>
import Ctx2 from '../../assets/js/ctx2'
import Matter from 'matter-js'
const Engine = Matter.Engine, // 引擎.引擎模块包含创建和操作引擎的方法
  Render = Matter.Render, // 基于HTML5画布的渲染器
  World = Matter.World, // 演出舞台. Matter.js 中任何的物体都需要一个舞台/容器，而存放这些物体的地方，则称之为World(世界)
  Bodies = Matter.Bodies, // 用于创建各种形状的物体，物体必须添加到Wolrd中，然后由引擎运行世界
  Body = Matter.Body,
  MouseConstraint = Matter.MouseConstraint,
  Mouse = Matter.Mouse,
  Sleeping = Matter.Sleeping,
  Events = Matter.Events

export default {
  data() {
    return {
      mouseValve: false,
      // 基本数据
      boxW: window.innerWidth,
      boxH: window.innerHeight,
      radius: [52 / 4, 80 / 4, 108 / 4, 118 / 4, 152 / 4, 184 / 4, 194 / 4, 258 / 4, 308 / 4, 310 / 4, 408 / 4],
      assets: [
        require('./img1/11.png'),
        require('./img1/10.png'),
        require('./img1/9.png'),
        require('./img1/8.png'),
        require('./img1/7.png'),
        require('./img1/6.png'),
        require('./img1/5.png'),
        require('./img1/4.png'),
        require('./img1/3.png'),
        require('./img1/2.png'),
        require('./img1/1.png'),
      ],
      canvas: null,
      circle: null,
      circleAmount: 0, // 小球出现的次数，可以根据次数的累计增加游戏难度，或者用于计算分数。
      canPlay: true,
      engine: null,
      world: null,
      render: null,
    }
  },
  mounted() {
    uni.setNavigationBarTitle({
      title: '合成大西瓜',
    })
    let wrap = document.querySelector('#stage')
    this.canvas = document.createElement('canvas')
    wrap.appendChild(this.canvas)
    this.init()
    this.addCircle()
    this.addEvents()
  },
  methods: {
    init() {
      this.engine = Engine.create({
        enableSleeping: true, // 在游戏结束检测的时候，用到该 sleep 功能，enableSleeping 为 true 可以检测到小球停止运动的状态，从而方便进行游戏结束的检测。
      })
      this.world = this.engine.world
      this.world.bounds = { min: { x: 0, y: 0 }, max: { x: this.boxW, y: this.boxH } }
      this.render = Render.create({
        canvas: this.canvas,
        engine: this.engine,
        options: {
          width: this.boxW,
          height: this.boxH,
          wireframes: false, // 设为 false 后，才可以展现出添加到小球的纹理
          background: '#ffe89d',
          showSleeping: false, // 隐藏 sleep 半透明的状态
        },
      })

      // 这里使用内置的长方形物体创建了游戏的墙壁和地面
      // 创建地面
      const ground = Bodies.rectangle(this.boxW / 2, this.boxH - 60 / 2, this.boxW, 60, {
        isStatic: true,
        render: {
          fillStyle: '#7b5438',
        },
      })
      const leftWall = Bodies.rectangle(-10 / 2, this.boxH / 2, 10, this.boxH, { isStatic: true })
      const rightWall = Bodies.rectangle(10 / 2 + this.boxW, this.boxH / 2, 10, this.boxH, { isStatic: true })
      // 将创建的物体添加到 World
      World.add(this.world, [ground, leftWall, rightWall])
      // 运行引擎与渲染器
      Matter.Runner.run(this.engine)
      Render.run(this.render)

      let ctx2 = new Ctx2(this.render.canvas.getContext('2d'))
      let _this = this

      function ticker() {
        ctx2.textFill(_this.circleAmount, 40, 40, ['#000'])
        requestAnimationFrame(ticker)
      }
      ticker()
    },
    // 添加球体
    addCircle() {
      const radiusTemp = this.radius.slice(0, 6)
      const index = this.circleAmount === 0 ? 0 : (Math.random() * radiusTemp.length) | 0
      const circleRadius = radiusTemp[index]
      this.circle = Bodies.circle(
        this.boxW / 2, // 小球的 x 坐标，这里是根据小球的圆心来定位的
        circleRadius + 30, // 小球的 y 坐标，将初始化的小球安置在水平居中，距离顶部 30 像素的位置
        circleRadius,
        {
          isStatic: true, // 首先设置为 true ，在触发事件之后再改为 false ，给小球添加下落动作
          restitution: 0.2, // 设置小球弹性
          render: {
            sprite: {
              texture: this.assets[index],
              xScale: 0.5,
              yScale: 0.5,
            },
          },
        }
      )
      World.add(this.world, this.circle)
      this.gameProgressChecking(this.circle) // 游戏状态检测，后续会说明
      this.circleAmount++
    },

    // 添加事件
    addEvents() {
      var mouse = Mouse.create(this.render.canvas)
      var mouseconstraint = MouseConstraint.create(this.engine, {
        mouse: mouse,
        constraint: {
          stiffness: 0.2,
          render: {
            visible: false,
          },
        },
      })
      this.render.mouse = mouse

      Events.on(mouseconstraint, 'mousedown', () => {
        this.mouseValve = true
      })
      // touchmove 事件
      Events.on(mouseconstraint, 'mousemove', (e) => {
        if (!this.mouseValve) return
        if (!this.circle || !this.canPlay) return // this.canPlay 判断游戏是否结束
        this.updateCirclePosition(e) // 在 touchmove 中更新小球的 x 坐标
      })
      // touchend 事件
      Events.on(mouseconstraint, 'mouseup', (e) => {
        this.mouseValve = false
        if (!this.circle || !this.canPlay) return
        this.updateCirclePosition(e)
        Sleeping.set(this.circle, false) // 接触小球的 sleep 模式，以便添加物理属性
        Body.setStatic(this.circle, false) // 给小球激活物理属性，小球会因为重力自动落下
        this.circle = null
        setTimeout(() => {
          this.addCircle()
        }, 1000)
      })

      Events.on(this.engine, 'collisionStart', (e) => this.collisionEvent(e)) // 下落的小球刚碰撞在一起的事件
      Events.on(this.engine, 'collisionActive', (e) => this.collisionEvent(e)) // 其他被动的小球相互碰撞的事件
    },

    // 碰撞检测
    collisionEvent(e) {
      if (!this.canPlay) return
      const { pairs } = e // pairs 为所有小球碰撞的集合，通过遍历该集合中参与碰撞的小球半径，就完成了逻辑判断
      Sleeping.afterCollisions(pairs) // 将参与碰撞的小球从休眠中激活
      for (let i = 0; i < pairs.length; i++) {
        const { bodyA, bodyB } = pairs[i] // 拿到参与碰撞的小球
        if (bodyA.circleRadius && bodyA.circleRadius == bodyB.circleRadius) {
          // 小球半径一致，变成更大的小球
          const {
            position: { x: bx, y: by },
            circleRadius,
          } = bodyA // 获取两个相同半径的小球，取中间位置合成大球
          const {
            position: { x: ax, y: ay },
          } = bodyB

          const x = (ax + bx) / 2
          const y = (ay + by) / 2

          const index = this.radius.indexOf(circleRadius) + 1
          this.circleAmount = this.circleAmount + index * 10

          const circleNew = Bodies.circle(x, y, this.radius[index], {
            // 创建大的球
            restitution: 0.2,
            render: {
              sprite: {
                texture: this.assets[index],
                xScale: 0.5,
                yScale: 0.5,
              },
            },
          })

          World.remove(this.world, bodyA) // 移除两个碰撞的小球
          World.remove(this.world, bodyB)
          World.add(this.world, circleNew) // 将生成的大球加入到 World
          this.gameProgressChecking(circleNew) // 判断游戏的状态
        }
      }
    },

    // 更新小球位置
    updateCirclePosition(e) {
      const xTemp = e.mouse.absolute.x
      const radius = this.circle.circleRadius
      Body.setPosition(this.circle, {
        x: xTemp < radius ? radius : xTemp + radius > this.boxW ? this.boxW - radius : xTemp,
        y: radius + 30,
      })
    },

    // 游戏状态检测
    gameProgressChecking(body) {
      // gameProgressChecking 在上文小球开始掉落的时候开始触发
      Events.on(body, 'sleepStart', (event) => {
        if (!event.source.isStatic && event.source.position.y <= 50) {
          // 如果小球静止时，y 坐标移除游戏区域，游戏结束
          this.gameOver()
        }
      })
    },

    // 游戏结束
    gameOver() {
      console.log('gameOver')
      this.canPlay = false
    },
  },
}
</script>
